@jaimevalasek/aioson 1.9.0 → 1.9.2
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/package.json +1 -1
- package/src/cli.js +8 -1
- package/src/commands/setup-context.js +6 -6
- package/src/commands/state-save.js +111 -13
- package/src/commands/update.js +6 -0
- package/src/constants.js +5 -1
- package/src/doctor.js +52 -0
- package/src/gateway-pointer-merge.js +103 -0
- package/src/i18n/messages/en.js +9 -2
- package/src/i18n/messages/es.js +9 -2
- package/src/i18n/messages/fr.js +9 -2
- package/src/i18n/messages/pt-BR.js +9 -2
- package/src/installer.js +39 -1
- package/src/jargon-leak-doctor.js +257 -0
- package/src/migrations/profile-rename.js +66 -0
- package/src/onboarding.js +7 -5
- package/src/parser.js +11 -2
- package/src/updater.js +21 -4
- package/template/.aioson/agents/analyst.md +12 -0
- package/template/.aioson/agents/dev.md +3 -0
- package/template/.aioson/agents/deyvin.md +9 -7
- package/template/.aioson/agents/neo.md +20 -5
- package/template/.aioson/agents/product.md +22 -6
- package/template/.aioson/agents/setup.md +6 -4
- package/template/.aioson/docs/deyvin/runtime-handoffs.md +6 -0
- package/template/.aioson/docs/handoff-persistence.md +94 -0
- package/template/.aioson/skills/process/decision-presentation/SKILL.md +119 -0
- package/template/.aioson/skills/process/decision-presentation/references/jargon-map.en.yaml +108 -0
- package/template/.aioson/skills/process/decision-presentation/references/jargon-map.pt-BR.yaml +108 -0
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -583,6 +583,8 @@ const JSON_SUPPORTED_COMMANDS = new Set([
|
|
|
583
583
|
'pulse-update',
|
|
584
584
|
'state:save',
|
|
585
585
|
'state-save',
|
|
586
|
+
'dev:state:write',
|
|
587
|
+
'dev-state-write',
|
|
586
588
|
'feature:close',
|
|
587
589
|
'feature-close',
|
|
588
590
|
'feature:archive',
|
|
@@ -1315,7 +1317,12 @@ async function main() {
|
|
|
1315
1317
|
result = await runDetectTestRunner({ args, options, logger: commandLogger });
|
|
1316
1318
|
} else if (command === 'pulse:update' || command === 'pulse-update') {
|
|
1317
1319
|
result = await runPulseUpdate({ args, options, logger: commandLogger });
|
|
1318
|
-
} else if (
|
|
1320
|
+
} else if (
|
|
1321
|
+
command === 'state:save' ||
|
|
1322
|
+
command === 'state-save' ||
|
|
1323
|
+
command === 'dev:state:write' ||
|
|
1324
|
+
command === 'dev-state-write'
|
|
1325
|
+
) {
|
|
1319
1326
|
result = await runStateSave({ args, options, logger: commandLogger });
|
|
1320
1327
|
} else if (command === 'feature:close' || command === 'feature-close') {
|
|
1321
1328
|
result = await runFeatureClose({ args, options, logger: commandLogger });
|
|
@@ -35,7 +35,7 @@ const {
|
|
|
35
35
|
inferProjectTypeFromFramework,
|
|
36
36
|
inferWeb3NetworkFromFramework,
|
|
37
37
|
buildDeveloperProfile,
|
|
38
|
-
|
|
38
|
+
recommendCreatorProfile,
|
|
39
39
|
buildTeamProfile
|
|
40
40
|
} = require('../onboarding');
|
|
41
41
|
|
|
@@ -286,7 +286,7 @@ function buildDeveloperProfileFromOptions(options, defaults, t) {
|
|
|
286
286
|
}
|
|
287
287
|
|
|
288
288
|
function buildBeginnerProfileFromOptions(options, t) {
|
|
289
|
-
const profile =
|
|
289
|
+
const profile = recommendCreatorProfile({
|
|
290
290
|
projectSummary: resolveOption(options, 'project-summary', ''),
|
|
291
291
|
expectedUsers: resolveOption(options, 'expected-users', ''),
|
|
292
292
|
mobileRequirement: resolveOption(options, 'mobile-requirement', ''),
|
|
@@ -408,7 +408,7 @@ async function askBeginnerProfile(rl, data, logger, t) {
|
|
|
408
408
|
const mobileRequirement = await ask(rl, t('setup_context.q_beginner_mobile'), '2');
|
|
409
409
|
const hostingPreference = await ask(rl, t('setup_context.q_beginner_hosting'), '1');
|
|
410
410
|
|
|
411
|
-
const recommendation =
|
|
411
|
+
const recommendation = recommendCreatorProfile({
|
|
412
412
|
projectSummary,
|
|
413
413
|
expectedUsers,
|
|
414
414
|
mobileRequirement,
|
|
@@ -443,7 +443,7 @@ async function askBeginnerProfile(rl, data, logger, t) {
|
|
|
443
443
|
|
|
444
444
|
return {
|
|
445
445
|
...custom,
|
|
446
|
-
profile: '
|
|
446
|
+
profile: 'creator',
|
|
447
447
|
notes: uniqueStrings([
|
|
448
448
|
...localizeProfileNotes(recommendation, t).notes,
|
|
449
449
|
t('setup_context.note_beginner_declined')
|
|
@@ -560,7 +560,7 @@ async function runSetupContext({ args, options, logger, t }) {
|
|
|
560
560
|
let profileData = null;
|
|
561
561
|
if (data.profile === 'developer') {
|
|
562
562
|
profileData = await askDeveloperProfile(rl, data, t);
|
|
563
|
-
} else if (data.profile === '
|
|
563
|
+
} else if (data.profile === 'creator') {
|
|
564
564
|
profileData = await askBeginnerProfile(rl, data, logger, t);
|
|
565
565
|
} else {
|
|
566
566
|
profileData = await askTeamProfile(rl, data, t);
|
|
@@ -601,7 +601,7 @@ async function runSetupContext({ args, options, logger, t }) {
|
|
|
601
601
|
let profileData = null;
|
|
602
602
|
if (profile === 'developer') {
|
|
603
603
|
profileData = buildDeveloperProfileFromOptions(options, data, t);
|
|
604
|
-
} else if (profile === '
|
|
604
|
+
} else if (profile === 'creator') {
|
|
605
605
|
profileData = buildBeginnerProfileFromOptions(options, t);
|
|
606
606
|
} else {
|
|
607
607
|
profileData = localizeProfileNotes(buildTeamProfileFromOptions(options, data), t);
|
|
@@ -1,25 +1,73 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* aioson state:save — create/update dev-state.md
|
|
4
|
+
* aioson state:save (alias: dev:state:write) — create/update dev-state.md
|
|
5
|
+
* for @dev session resumption.
|
|
5
6
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
7
|
+
* Stores active feature, phase, next step, spec version, and context package.
|
|
8
|
+
* Invoked by upstream agents (@analyst, @product, @sheldon, @architect) at
|
|
9
|
+
* session end so the next @dev activation auto-resumes on `next_step` instead
|
|
10
|
+
* of cold-starting.
|
|
8
11
|
*
|
|
9
12
|
* Usage:
|
|
10
|
-
* aioson state:
|
|
11
|
-
* --
|
|
12
|
-
* aioson state:save . --feature=checkout --next="Continue payment webhook"
|
|
13
|
+
* aioson dev:state:write . --feature=checkout --phase=3 \
|
|
14
|
+
* --next="Implement notification listeners" --context=spec,requirements,impl-plan
|
|
15
|
+
* aioson state:save . --feature=checkout --next="Continue payment webhook"
|
|
16
|
+
*
|
|
17
|
+
* --context (optional): comma-separated canonical type tokens. Each token
|
|
18
|
+
* resolves to a feature-scoped path. Max 4 entries total (including the
|
|
19
|
+
* always-included project.context.md anchor). Unknown tokens or missing
|
|
20
|
+
* files emit a warning and are skipped — never fail the command.
|
|
21
|
+
*
|
|
22
|
+
* Canonical context tokens:
|
|
23
|
+
* prd → prd-{slug}.md
|
|
24
|
+
* requirements → requirements-{slug}.md
|
|
25
|
+
* spec → spec-{slug}.md
|
|
26
|
+
* architecture → architecture.md
|
|
27
|
+
* impl-plan → implementation-plan-{slug}.md
|
|
28
|
+
* sheldon → sheldon-enrichment-{slug}.md
|
|
29
|
+
* design-doc → design-doc-{slug}.md (falls back to design-doc.md)
|
|
30
|
+
* dossier → features/{slug}/dossier.md
|
|
31
|
+
*
|
|
32
|
+
* When --context is omitted, auto-detect kicks in (legacy behavior): include
|
|
33
|
+
* project.context.md + spec-{slug}.md + plan if present.
|
|
13
34
|
*/
|
|
14
35
|
|
|
15
36
|
const fs = require('node:fs/promises');
|
|
16
37
|
const path = require('node:path');
|
|
17
38
|
const { contextDir, readFileSafe, parseFrontmatter, scanArtifacts } = require('../preflight-engine');
|
|
18
39
|
|
|
40
|
+
const MAX_CONTEXT = 4;
|
|
41
|
+
|
|
42
|
+
const CONTEXT_TYPE_MAP = {
|
|
43
|
+
prd: { rel: (slug) => `prd-${slug}.md` },
|
|
44
|
+
requirements: { rel: (slug) => `requirements-${slug}.md` },
|
|
45
|
+
spec: { rel: (slug) => `spec-${slug}.md` },
|
|
46
|
+
architecture: { rel: () => 'architecture.md' },
|
|
47
|
+
'impl-plan': { rel: (slug) => `implementation-plan-${slug}.md` },
|
|
48
|
+
sheldon: { rel: (slug) => `sheldon-enrichment-${slug}.md` },
|
|
49
|
+
'design-doc': { rel: (slug) => `design-doc-${slug}.md`, fallback: () => 'design-doc.md' },
|
|
50
|
+
dossier: { rel: (slug) => `features/${slug}/dossier.md` }
|
|
51
|
+
};
|
|
52
|
+
|
|
19
53
|
function nowDate() {
|
|
20
54
|
return new Date().toISOString().slice(0, 10);
|
|
21
55
|
}
|
|
22
56
|
|
|
57
|
+
function parseContextFlag(value) {
|
|
58
|
+
if (value === undefined || value === null || value === '') return null;
|
|
59
|
+
return String(value).split(',').map((s) => s.trim()).filter(Boolean);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async function fileExistsRel(targetDir, rel) {
|
|
63
|
+
try {
|
|
64
|
+
await fs.access(path.join(contextDir(targetDir), rel));
|
|
65
|
+
return true;
|
|
66
|
+
} catch {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
23
71
|
async function runStateSave({ args, options = {}, logger }) {
|
|
24
72
|
const targetDir = path.resolve(process.cwd(), args[0] || '.');
|
|
25
73
|
const slug = options.feature ? String(options.feature) : null;
|
|
@@ -28,6 +76,7 @@ async function runStateSave({ args, options = {}, logger }) {
|
|
|
28
76
|
const specVersion = options['spec-version'] ? String(options['spec-version']) : null;
|
|
29
77
|
const status = options.status ? String(options.status) : 'in_progress';
|
|
30
78
|
const plan = options.plan ? String(options.plan) : null;
|
|
79
|
+
const contextTokens = parseContextFlag(options.context);
|
|
31
80
|
|
|
32
81
|
if (!slug) {
|
|
33
82
|
if (options.json) return { ok: false, reason: 'missing_feature' };
|
|
@@ -41,13 +90,49 @@ async function runStateSave({ args, options = {}, logger }) {
|
|
|
41
90
|
return { ok: false };
|
|
42
91
|
}
|
|
43
92
|
|
|
44
|
-
// Build context package
|
|
93
|
+
// Build context package
|
|
45
94
|
const artifacts = await scanArtifacts(targetDir, slug);
|
|
46
95
|
const contextPackage = [];
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
if (
|
|
50
|
-
|
|
96
|
+
const warnings = [];
|
|
97
|
+
|
|
98
|
+
if (contextTokens) {
|
|
99
|
+
// Explicit mode: agent declares the canonical types to include.
|
|
100
|
+
// Always anchor with project.context.md (counts toward the 4-entry cap).
|
|
101
|
+
if (artifacts.project_context.exists) contextPackage.push('project.context.md');
|
|
102
|
+
|
|
103
|
+
for (const rawToken of contextTokens) {
|
|
104
|
+
const token = rawToken.toLowerCase();
|
|
105
|
+
if (contextPackage.length >= MAX_CONTEXT) {
|
|
106
|
+
warnings.push(`context cap reached (${MAX_CONTEXT}); skipped "${token}" and remaining`);
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
const def = CONTEXT_TYPE_MAP[token];
|
|
110
|
+
if (!def) {
|
|
111
|
+
warnings.push(`unknown context type "${token}" (valid: ${Object.keys(CONTEXT_TYPE_MAP).join(', ')})`);
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
const relPath = def.rel(slug);
|
|
115
|
+
if (await fileExistsRel(targetDir, relPath)) {
|
|
116
|
+
if (!contextPackage.includes(relPath)) contextPackage.push(relPath);
|
|
117
|
+
} else if (def.fallback) {
|
|
118
|
+
const fb = def.fallback();
|
|
119
|
+
if (await fileExistsRel(targetDir, fb)) {
|
|
120
|
+
if (!contextPackage.includes(fb)) contextPackage.push(fb);
|
|
121
|
+
} else {
|
|
122
|
+
warnings.push(`"${token}" file missing (${relPath}); skipped`);
|
|
123
|
+
}
|
|
124
|
+
} else {
|
|
125
|
+
warnings.push(`"${token}" file missing (${relPath}); skipped`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
} else {
|
|
129
|
+
// Auto-detect (legacy behavior): keep backward compatibility for callers
|
|
130
|
+
// that haven't migrated to --context yet.
|
|
131
|
+
if (artifacts.project_context.exists) contextPackage.push('project.context.md');
|
|
132
|
+
if (artifacts.spec.exists) contextPackage.push(`spec-${slug}.md`);
|
|
133
|
+
if (plan) contextPackage.push(plan);
|
|
134
|
+
else if (artifacts.implementation_plan.exists) contextPackage.push(`implementation-plan-${slug}.md`);
|
|
135
|
+
}
|
|
51
136
|
|
|
52
137
|
const today = nowDate();
|
|
53
138
|
const statePath = path.join(contextDir(targetDir), 'dev-state.md');
|
|
@@ -104,7 +189,8 @@ async function runStateSave({ args, options = {}, logger }) {
|
|
|
104
189
|
active_phase: phase,
|
|
105
190
|
next_step: next,
|
|
106
191
|
last_spec_version: specVersion,
|
|
107
|
-
context_package: contextPackage
|
|
192
|
+
context_package: contextPackage,
|
|
193
|
+
warnings
|
|
108
194
|
};
|
|
109
195
|
|
|
110
196
|
if (options.json) return result;
|
|
@@ -115,8 +201,20 @@ async function runStateSave({ args, options = {}, logger }) {
|
|
|
115
201
|
logger.log(` next_step: "${next}"`);
|
|
116
202
|
if (specVersion) logger.log(` last_spec_version: ${specVersion}`);
|
|
117
203
|
logger.log(` context_package: [${contextPackage.join(', ')}]`);
|
|
204
|
+
for (const w of warnings) logger.log(` warn: ${w}`);
|
|
205
|
+
if (warnings.length === 0 && contextTokens) {
|
|
206
|
+
// Visible confirmation banner — agent kernels can rely on this to know
|
|
207
|
+
// they fulfilled the dev-state.md producer contract for @dev cold-resume.
|
|
208
|
+
const preview = next.length > 80 ? `${next.slice(0, 77)}...` : next;
|
|
209
|
+
logger.log(` ✓ @dev will auto-resume on cold start: next_step="${preview}"`);
|
|
210
|
+
}
|
|
118
211
|
|
|
119
212
|
return result;
|
|
120
213
|
}
|
|
121
214
|
|
|
122
|
-
module.exports = {
|
|
215
|
+
module.exports = {
|
|
216
|
+
runStateSave,
|
|
217
|
+
CONTEXT_TYPE_MAP,
|
|
218
|
+
MAX_CONTEXT,
|
|
219
|
+
parseContextFlag
|
|
220
|
+
};
|
package/src/commands/update.js
CHANGED
|
@@ -10,12 +10,14 @@ async function runUpdate({ args, options, logger, t }) {
|
|
|
10
10
|
const targetDir = path.resolve(process.cwd(), args[0] || '.');
|
|
11
11
|
const dryRun = Boolean(options['dry-run']);
|
|
12
12
|
const all = Boolean(options.all);
|
|
13
|
+
const selective = Boolean(options.selective);
|
|
13
14
|
const requestedLanguage = options.lang || options.language;
|
|
14
15
|
|
|
15
16
|
const detection = await detectFramework(targetDir);
|
|
16
17
|
const result = await updateInstallation(targetDir, {
|
|
17
18
|
dryRun,
|
|
18
19
|
all,
|
|
20
|
+
selective,
|
|
19
21
|
frameworkDetection: detection.framework
|
|
20
22
|
});
|
|
21
23
|
|
|
@@ -40,6 +42,10 @@ async function runUpdate({ args, options, logger, t }) {
|
|
|
40
42
|
logger.log(t('update.done_at', { targetDir }));
|
|
41
43
|
logger.log(t('update.files_updated', { count: result.copied.length }));
|
|
42
44
|
logger.log(t('update.backups_created', { count: result.backedUp.length }));
|
|
45
|
+
if (result.migrations && result.migrations.profileRename && result.migrations.profileRename.changed) {
|
|
46
|
+
logger.log('');
|
|
47
|
+
logger.log(t('update.profile_renamed'));
|
|
48
|
+
}
|
|
43
49
|
if (!dryRun) {
|
|
44
50
|
logger.log('');
|
|
45
51
|
logger.log(t('update.reconfigure_hint'));
|
package/src/constants.js
CHANGED
|
@@ -60,6 +60,7 @@ const MANAGED_FILES = [
|
|
|
60
60
|
'.aioson/docs/deyvin/pair-execution.md',
|
|
61
61
|
'.aioson/docs/deyvin/runtime-handoffs.md',
|
|
62
62
|
'.aioson/docs/deyvin/debugging-escalation.md',
|
|
63
|
+
'.aioson/docs/handoff-persistence.md',
|
|
63
64
|
'.aioson/docs/sheldon/research-loop.md',
|
|
64
65
|
'.aioson/docs/sheldon/web-intelligence.md',
|
|
65
66
|
'.aioson/docs/sheldon/quality-lens.md',
|
|
@@ -67,6 +68,9 @@ const MANAGED_FILES = [
|
|
|
67
68
|
'.aioson/docs/sheldon/harness-contract.md',
|
|
68
69
|
'.aioson/docs/dev/stack-conventions.md',
|
|
69
70
|
'.aioson/docs/dev/execution-discipline.md',
|
|
71
|
+
'.aioson/skills/process/decision-presentation/SKILL.md',
|
|
72
|
+
'.aioson/skills/process/decision-presentation/references/jargon-map.en.yaml',
|
|
73
|
+
'.aioson/skills/process/decision-presentation/references/jargon-map.pt-BR.yaml',
|
|
70
74
|
'.aioson/skills/static/laravel-conventions.md',
|
|
71
75
|
'.aioson/skills/static/tall-stack-patterns.md',
|
|
72
76
|
'.aioson/skills/static/jetstream-setup.md',
|
|
@@ -180,7 +184,7 @@ const CONTEXT_REQUIRED_FIELDS = [
|
|
|
180
184
|
|
|
181
185
|
const CONTEXT_ALLOWED_CLASSIFICATIONS = ['MICRO', 'SMALL', 'MEDIUM'];
|
|
182
186
|
const CONTEXT_ALLOWED_PROJECT_TYPES = ['web_app', 'api', 'site', 'script', 'dapp', 'desktop_app'];
|
|
183
|
-
const CONTEXT_ALLOWED_PROFILES = ['developer', '
|
|
187
|
+
const CONTEXT_ALLOWED_PROFILES = ['developer', 'creator', 'team'];
|
|
184
188
|
|
|
185
189
|
const AGENT_DEFINITIONS = [
|
|
186
190
|
{
|
package/src/doctor.js
CHANGED
|
@@ -18,6 +18,7 @@ const {
|
|
|
18
18
|
assessLearningOrphans,
|
|
19
19
|
assessDistillationLag
|
|
20
20
|
} = require('./learning-loop-doctor');
|
|
21
|
+
const { assessJargonLeak } = require('./jargon-leak-doctor');
|
|
21
22
|
const { openRuntimeDb } = require('./runtime-store');
|
|
22
23
|
|
|
23
24
|
const BOOTSTRAP_REQUIRED = ['what-is.md', 'how-it-works.md', 'what-it-does.md', 'current-state.md'];
|
|
@@ -436,6 +437,57 @@ async function runDoctor(targetDir) {
|
|
|
436
437
|
}
|
|
437
438
|
}
|
|
438
439
|
|
|
440
|
+
// 8. lay-user-agent-mode — jargon_leak_detection (Phase 3)
|
|
441
|
+
// Independent of classification (the skip rule is profile-based, not
|
|
442
|
+
// size-based). Opens its own DB handle so it works for MICRO projects.
|
|
443
|
+
let jargonDb = null;
|
|
444
|
+
try {
|
|
445
|
+
try {
|
|
446
|
+
const handle = await openRuntimeDb(targetDir);
|
|
447
|
+
jargonDb = handle.db;
|
|
448
|
+
} catch {
|
|
449
|
+
jargonDb = null; // greenfield (EC-LUM-05)
|
|
450
|
+
}
|
|
451
|
+
const jargonAssessment = await assessJargonLeak({ db: jargonDb, targetDir });
|
|
452
|
+
if (jargonAssessment.skipped) {
|
|
453
|
+
checks.push({
|
|
454
|
+
id: 'jargon_leak_detection',
|
|
455
|
+
severity: 'warning',
|
|
456
|
+
key: 'doctor.jargon_leak_detection.skipped_dev',
|
|
457
|
+
params: { profile: jargonAssessment.profile },
|
|
458
|
+
ok: true
|
|
459
|
+
});
|
|
460
|
+
} else {
|
|
461
|
+
const params = {
|
|
462
|
+
count: jargonAssessment.count,
|
|
463
|
+
events: jargonAssessment.eventsScanned || 0,
|
|
464
|
+
profile: jargonAssessment.profile
|
|
465
|
+
};
|
|
466
|
+
checks.push({
|
|
467
|
+
id: 'jargon_leak_detection',
|
|
468
|
+
severity: 'warning',
|
|
469
|
+
key: jargonAssessment.ok
|
|
470
|
+
? 'doctor.jargon_leak_detection.ok'
|
|
471
|
+
: 'doctor.jargon_leak_detection.fail',
|
|
472
|
+
params,
|
|
473
|
+
ok: jargonAssessment.ok,
|
|
474
|
+
hintKey: jargonAssessment.ok ? undefined : 'doctor.jargon_leak_detection.hint',
|
|
475
|
+
hintParams: jargonAssessment.ok
|
|
476
|
+
? undefined
|
|
477
|
+
: {
|
|
478
|
+
samples: jargonAssessment.samples
|
|
479
|
+
.slice(0, 5)
|
|
480
|
+
.map((s) => `${s.agent}/${(s.terms || []).join('+')}`)
|
|
481
|
+
.join(', ') || '(none)'
|
|
482
|
+
}
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
} finally {
|
|
486
|
+
if (jargonDb) {
|
|
487
|
+
try { jargonDb.close(); } catch { /* swallow */ }
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
439
491
|
// 5. permissions_in_sync
|
|
440
492
|
const permsAssessment = await assessPermissionsSync(targetDir);
|
|
441
493
|
const permsOk = !permsAssessment.protocolMissing
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Gateway pointer merge — keeps the AIOSON instructions inside CLAUDE.md,
|
|
4
|
+
// AGENTS.md, OPENCODE.md, and .gemini/GEMINI.md without clobbering any
|
|
5
|
+
// project-authored content above the managed block. When the destination
|
|
6
|
+
// file already exists, the prior copy path skipped it entirely, leaving
|
|
7
|
+
// existing projects without the framework instructions. This module wraps
|
|
8
|
+
// the template body in <!-- AIOSON:BEGIN --> ... <!-- AIOSON:END --> markers
|
|
9
|
+
// and either appends or replaces that block in place.
|
|
10
|
+
|
|
11
|
+
const fs = require('node:fs/promises');
|
|
12
|
+
const path = require('node:path');
|
|
13
|
+
const { exists, copyFileWithDir, toRelativeSafe } = require('./utils');
|
|
14
|
+
|
|
15
|
+
const MARKER_BEGIN = '<!-- AIOSON:BEGIN -->';
|
|
16
|
+
const MARKER_END = '<!-- AIOSON:END -->';
|
|
17
|
+
const BLOCK_NOTICE = '> Managed by AIOSON — edits inside this block will be overwritten on `aioson update`. Put project-specific rules above or below this block.';
|
|
18
|
+
|
|
19
|
+
const GATEWAY_POINTER_FILES = new Set([
|
|
20
|
+
'CLAUDE.md',
|
|
21
|
+
'AGENTS.md',
|
|
22
|
+
'OPENCODE.md',
|
|
23
|
+
'.gemini/GEMINI.md'
|
|
24
|
+
]);
|
|
25
|
+
|
|
26
|
+
function isGatewayPointerPath(rel) {
|
|
27
|
+
return GATEWAY_POINTER_FILES.has(rel);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function buildBlock(templateContent) {
|
|
31
|
+
const body = templateContent.endsWith('\n') ? templateContent : `${templateContent}\n`;
|
|
32
|
+
return `${MARKER_BEGIN}\n${BLOCK_NOTICE}\n\n${body}${MARKER_END}\n`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function findBlockRange(content) {
|
|
36
|
+
const start = content.indexOf(MARKER_BEGIN);
|
|
37
|
+
if (start === -1) return null;
|
|
38
|
+
const endIdx = content.indexOf(MARKER_END, start + MARKER_BEGIN.length);
|
|
39
|
+
if (endIdx === -1) return null;
|
|
40
|
+
let end = endIdx + MARKER_END.length;
|
|
41
|
+
if (content[end] === '\n') end += 1;
|
|
42
|
+
return { start, end };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function mergeGatewayPointer({ templatePath, targetPath, backupRoot, targetDir, dryRun = false }) {
|
|
46
|
+
const templateContent = await fs.readFile(templatePath, 'utf8');
|
|
47
|
+
const block = buildBlock(templateContent);
|
|
48
|
+
|
|
49
|
+
if (!(await exists(targetPath))) {
|
|
50
|
+
if (!dryRun) await fs.writeFile(targetPath, block, 'utf8');
|
|
51
|
+
return { action: 'created' };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const existing = await fs.readFile(targetPath, 'utf8');
|
|
55
|
+
const range = findBlockRange(existing);
|
|
56
|
+
|
|
57
|
+
let next;
|
|
58
|
+
let action;
|
|
59
|
+
if (range) {
|
|
60
|
+
const before = existing.slice(0, range.start);
|
|
61
|
+
const after = existing.slice(range.end);
|
|
62
|
+
const cleanBefore = before.length === 0 || before.endsWith('\n') ? before : `${before}\n`;
|
|
63
|
+
next = `${cleanBefore}${block}${after}`;
|
|
64
|
+
action = 'block_updated';
|
|
65
|
+
} else {
|
|
66
|
+
const separator = existing.length === 0 ? '' : existing.endsWith('\n\n') ? '' : existing.endsWith('\n') ? '\n' : '\n\n';
|
|
67
|
+
next = `${existing}${separator}${block}`;
|
|
68
|
+
action = 'block_appended';
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (next === existing) return { action: 'unchanged' };
|
|
72
|
+
|
|
73
|
+
let backupPath = null;
|
|
74
|
+
let backupError = null;
|
|
75
|
+
if (backupRoot && targetDir) {
|
|
76
|
+
const rel = toRelativeSafe(targetDir, targetPath);
|
|
77
|
+
const dest = path.join(backupRoot, rel);
|
|
78
|
+
if (!dryRun) {
|
|
79
|
+
try {
|
|
80
|
+
await copyFileWithDir(targetPath, dest);
|
|
81
|
+
backupPath = dest;
|
|
82
|
+
} catch (err) {
|
|
83
|
+
backupError = err && err.message ? err.message : String(err);
|
|
84
|
+
}
|
|
85
|
+
} else {
|
|
86
|
+
backupPath = dest;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (!dryRun) await fs.writeFile(targetPath, next, 'utf8');
|
|
91
|
+
return { action, backupPath, backupError };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
module.exports = {
|
|
95
|
+
MARKER_BEGIN,
|
|
96
|
+
MARKER_END,
|
|
97
|
+
BLOCK_NOTICE,
|
|
98
|
+
GATEWAY_POINTER_FILES,
|
|
99
|
+
isGatewayPointerPath,
|
|
100
|
+
buildBlock,
|
|
101
|
+
findBlockRange,
|
|
102
|
+
mergeGatewayPointer
|
|
103
|
+
};
|
package/src/i18n/messages/en.js
CHANGED
|
@@ -343,6 +343,7 @@ module.exports = {
|
|
|
343
343
|
done_at: 'Update completed at: {targetDir}',
|
|
344
344
|
files_updated: 'Files updated: {count}',
|
|
345
345
|
backups_created: 'Backups created: {count}',
|
|
346
|
+
profile_renamed: 'i Profile `beginner` renamed to `creator` in project.context.md to better describe the user. Behavior unchanged. Edit the file to switch to `developer` if desired.',
|
|
346
347
|
reconfigure_hint: 'New options may be available. Run: aioson install --reconfigure'
|
|
347
348
|
},
|
|
348
349
|
info: {
|
|
@@ -381,7 +382,7 @@ module.exports = {
|
|
|
381
382
|
context_project_type_value: '`project_type` must be one of {expected}',
|
|
382
383
|
context_project_type_value_hint: 'Use web_app, api, site, script, dapp, or desktop_app exactly.',
|
|
383
384
|
context_profile_value: '`profile` must be one of {expected}',
|
|
384
|
-
context_profile_value_hint: 'Use developer,
|
|
385
|
+
context_profile_value_hint: 'Use developer, creator, or team exactly.',
|
|
385
386
|
context_interaction_language_format: '`interaction_language` is not a valid BCP-47 tag',
|
|
386
387
|
context_interaction_language_format_hint: 'Use values like en, en-US, pt-BR.',
|
|
387
388
|
context_conversation_language_format: '`conversation_language` is not a valid BCP-47 tag',
|
|
@@ -452,6 +453,12 @@ module.exports = {
|
|
|
452
453
|
distillation_lag: 'Distillation lag: {closed} closed features but only {distillations} have an auto_distillation event (threshold {threshold})',
|
|
453
454
|
distillation_lag_hint: 'Features missing distillation (first 5): {missing_slugs}. Check Phase 5 hook health.',
|
|
454
455
|
distillation_lag_skipped_micro: 'Distillation lag check skipped: project classification is MICRO (BR-ALL-11)'
|
|
456
|
+
},
|
|
457
|
+
jargon_leak_detection: {
|
|
458
|
+
ok: 'No jargon leaks in user-facing agent events ({events} events scanned, profile={profile})',
|
|
459
|
+
fail: 'Jargon leaks: {count} occurrences across {events} events from MVP agents (profile={profile})',
|
|
460
|
+
hint: 'Affected events (first 5): {samples}. Translate the term via jargon-map.{en,pt-BR}.yaml or update the dictionary if the term is intentional.',
|
|
461
|
+
skipped_dev: 'Jargon leak check skipped: project profile is `{profile}` (jargon permitted in this mode)'
|
|
455
462
|
}
|
|
456
463
|
},
|
|
457
464
|
i18n_add: {
|
|
@@ -533,7 +540,7 @@ module.exports = {
|
|
|
533
540
|
detected: 'Detected framework: {framework} (installed={installed})',
|
|
534
541
|
q_project_name: 'Project name',
|
|
535
542
|
q_project_type: 'Project type (web_app|api|site|script|dapp|desktop_app)',
|
|
536
|
-
q_profile: 'Profile: [1] developer [2]
|
|
543
|
+
q_profile: 'Profile: [1] developer [2] creator [3] team',
|
|
537
544
|
q_use_detected_framework: 'Use detected framework? (true/false)',
|
|
538
545
|
q_framework: 'Framework',
|
|
539
546
|
q_framework_installed: 'Framework installed? (true/false)',
|
package/src/i18n/messages/es.js
CHANGED
|
@@ -217,6 +217,7 @@ module.exports = {
|
|
|
217
217
|
done_at: 'Actualizacion completada en: {targetDir}',
|
|
218
218
|
files_updated: 'Archivos actualizados: {count}',
|
|
219
219
|
backups_created: 'Backups creados: {count}',
|
|
220
|
+
profile_renamed: 'i Perfil `beginner` renombrado a `creator` en project.context.md para describir mejor al usuario. Comportamiento sin cambios. Edita el archivo para cambiar a `developer` si lo prefieres.',
|
|
220
221
|
reconfigure_hint: 'Nuevas opciones pueden estar disponibles. Ejecuta: aioson install --reconfigure'
|
|
221
222
|
},
|
|
222
223
|
info: {
|
|
@@ -259,7 +260,7 @@ module.exports = {
|
|
|
259
260
|
context_project_type_value: '`project_type` debe ser uno de {expected}',
|
|
260
261
|
context_project_type_value_hint: 'Usa web_app, api, site, script, dapp o desktop_app exactamente.',
|
|
261
262
|
context_profile_value: '`profile` debe ser uno de {expected}',
|
|
262
|
-
context_profile_value_hint: 'Usa developer,
|
|
263
|
+
context_profile_value_hint: 'Usa developer, creator o team exactamente.',
|
|
263
264
|
context_interaction_language_format:
|
|
264
265
|
'`interaction_language` no es una etiqueta BCP-47 valida',
|
|
265
266
|
context_interaction_language_format_hint: 'Usa valores como en, en-US, pt-BR.',
|
|
@@ -330,6 +331,12 @@ module.exports = {
|
|
|
330
331
|
distillation_lag: 'Retraso de distillation: {closed} features cerradas pero solo {distillations} tienen evento auto_distillation (umbral {threshold})',
|
|
331
332
|
distillation_lag_hint: 'Features sin distillation (primeras 5): {missing_slugs}. Verifique el hook de la Phase 5.',
|
|
332
333
|
distillation_lag_skipped_micro: 'Verificación de distillation_lag omitida: clasificación del proyecto es MICRO (BR-ALL-11)'
|
|
334
|
+
},
|
|
335
|
+
jargon_leak_detection: {
|
|
336
|
+
ok: 'Sin fugas de jerga en eventos de agentes user-facing ({events} eventos analizados, profile={profile})',
|
|
337
|
+
fail: 'Fugas de jerga: {count} ocurrencias en {events} eventos de agentes del MVP (profile={profile})',
|
|
338
|
+
hint: 'Eventos afectados (primeros 5): {samples}. Traduce el término vía jargon-map.{en,pt-BR}.yaml o actualiza el diccionario si el término es intencional.',
|
|
339
|
+
skipped_dev: 'Verificación de jargon_leak_detection omitida: profile del proyecto es `{profile}` (jerga permitida en este modo)'
|
|
333
340
|
}
|
|
334
341
|
},
|
|
335
342
|
i18n_add: {
|
|
@@ -411,7 +418,7 @@ module.exports = {
|
|
|
411
418
|
detected: 'Framework detectado: {framework} (installed={installed})',
|
|
412
419
|
q_project_name: 'Nombre del proyecto',
|
|
413
420
|
q_project_type: 'Tipo de proyecto (web_app|api|site|script|dapp|desktop_app)',
|
|
414
|
-
q_profile: 'Perfil: [1] developer [2]
|
|
421
|
+
q_profile: 'Perfil: [1] developer [2] creator [3] team',
|
|
415
422
|
q_use_detected_framework: 'Usar framework detectado? (true/false)',
|
|
416
423
|
q_framework: 'Framework',
|
|
417
424
|
q_framework_installed: 'Framework instalado? (true/false)',
|
package/src/i18n/messages/fr.js
CHANGED
|
@@ -216,6 +216,7 @@ module.exports = {
|
|
|
216
216
|
done_at: 'Mise a jour terminee dans : {targetDir}',
|
|
217
217
|
files_updated: 'Fichiers mis a jour : {count}',
|
|
218
218
|
backups_created: 'Sauvegardes creees : {count}',
|
|
219
|
+
profile_renamed: 'i Profil `beginner` renomme en `creator` dans project.context.md pour mieux decrire l utilisateur. Comportement inchange. Modifiez le fichier pour passer a `developer` si vous le souhaitez.',
|
|
219
220
|
reconfigure_hint: 'De nouvelles options peuvent etre disponibles. Lancez : aioson install --reconfigure'
|
|
220
221
|
},
|
|
221
222
|
info: {
|
|
@@ -258,7 +259,7 @@ module.exports = {
|
|
|
258
259
|
context_project_type_value: '`project_type` doit etre une des valeurs {expected}',
|
|
259
260
|
context_project_type_value_hint: 'Utilisez web_app, api, site, script, dapp ou desktop_app exactement.',
|
|
260
261
|
context_profile_value: '`profile` doit etre une des valeurs {expected}',
|
|
261
|
-
context_profile_value_hint: 'Utilisez developer,
|
|
262
|
+
context_profile_value_hint: 'Utilisez developer, creator ou team exactement.',
|
|
262
263
|
context_interaction_language_format:
|
|
263
264
|
'`interaction_language` n est pas une balise BCP-47 valide',
|
|
264
265
|
context_interaction_language_format_hint: 'Utilisez des valeurs comme en, en-US, pt-BR.',
|
|
@@ -329,6 +330,12 @@ module.exports = {
|
|
|
329
330
|
distillation_lag: 'Retard distillation : {closed} features fermées mais seulement {distillations} ont un évènement auto_distillation (seuil {threshold})',
|
|
330
331
|
distillation_lag_hint: 'Features sans distillation (5 premières) : {missing_slugs}. Vérifiez le hook de la Phase 5.',
|
|
331
332
|
distillation_lag_skipped_micro: 'Vérification distillation_lag ignorée : classification du projet est MICRO (BR-ALL-11)'
|
|
333
|
+
},
|
|
334
|
+
jargon_leak_detection: {
|
|
335
|
+
ok: 'Aucune fuite de jargon dans les évènements des agents user-facing ({events} évènements analysés, profile={profile})',
|
|
336
|
+
fail: 'Fuites de jargon : {count} occurrences dans {events} évènements des agents du MVP (profile={profile})',
|
|
337
|
+
hint: 'Évènements affectés (5 premiers) : {samples}. Traduisez le terme via jargon-map.{en,pt-BR}.yaml ou mettez à jour le dictionnaire si le terme est intentionnel.',
|
|
338
|
+
skipped_dev: 'Vérification jargon_leak_detection ignorée : profile du projet est `{profile}` (jargon autorisé dans ce mode)'
|
|
332
339
|
}
|
|
333
340
|
},
|
|
334
341
|
i18n_add: {
|
|
@@ -410,7 +417,7 @@ module.exports = {
|
|
|
410
417
|
detected: 'Framework detecte : {framework} (installed={installed})',
|
|
411
418
|
q_project_name: 'Nom du projet',
|
|
412
419
|
q_project_type: 'Type de projet (web_app|api|site|script|dapp|desktop_app)',
|
|
413
|
-
q_profile: 'Profil : [1] developer [2]
|
|
420
|
+
q_profile: 'Profil : [1] developer [2] creator [3] team',
|
|
414
421
|
q_use_detected_framework: 'Utiliser le framework detecte ? (true/false)',
|
|
415
422
|
q_framework: 'Framework',
|
|
416
423
|
q_framework_installed: 'Framework installe ? (true/false)',
|
|
@@ -310,6 +310,7 @@ module.exports = {
|
|
|
310
310
|
done_at: 'Atualizacao concluida em: {targetDir}',
|
|
311
311
|
files_updated: 'Arquivos atualizados: {count}',
|
|
312
312
|
backups_created: 'Backups criados: {count}',
|
|
313
|
+
profile_renamed: 'i Perfil `beginner` renomeado para `creator` em project.context.md para descrever melhor o usuario. Comportamento inalterado. Edite o arquivo para mudar para `developer` se preferir.',
|
|
313
314
|
reconfigure_hint: 'Novas opcoes podem estar disponiveis. Execute: aioson install --reconfigure'
|
|
314
315
|
},
|
|
315
316
|
info: {
|
|
@@ -352,7 +353,7 @@ module.exports = {
|
|
|
352
353
|
context_project_type_value: '`project_type` deve ser um de {expected}',
|
|
353
354
|
context_project_type_value_hint: 'Use web_app, api, site, script, dapp ou desktop_app exatamente.',
|
|
354
355
|
context_profile_value: '`profile` deve ser um de {expected}',
|
|
355
|
-
context_profile_value_hint: 'Use developer,
|
|
356
|
+
context_profile_value_hint: 'Use developer, creator ou team exatamente.',
|
|
356
357
|
context_interaction_language_format:
|
|
357
358
|
'`interaction_language` nao e uma tag BCP-47 valida',
|
|
358
359
|
context_interaction_language_format_hint: 'Use valores como en, en-US, pt-BR.',
|
|
@@ -424,6 +425,12 @@ module.exports = {
|
|
|
424
425
|
distillation_lag: 'Lag de distillation: {closed} features fechadas mas apenas {distillations} têm evento auto_distillation (limite {threshold})',
|
|
425
426
|
distillation_lag_hint: 'Features sem distillation (primeiras 5): {missing_slugs}. Verifique o hook da Phase 5.',
|
|
426
427
|
distillation_lag_skipped_micro: 'Check de distillation_lag ignorado: classificação do projeto é MICRO (BR-ALL-11)'
|
|
428
|
+
},
|
|
429
|
+
jargon_leak_detection: {
|
|
430
|
+
ok: 'Sem vazamento de jargão nos eventos dos agentes user-facing ({events} eventos analisados, profile={profile})',
|
|
431
|
+
fail: 'Vazamento de jargão: {count} ocorrências em {events} eventos dos agentes do MVP (profile={profile})',
|
|
432
|
+
hint: 'Eventos afetados (primeiros 5): {samples}. Traduza o termo via jargon-map.{en,pt-BR}.yaml ou atualize o dicionário se o termo for intencional.',
|
|
433
|
+
skipped_dev: 'Check de jargon_leak_detection ignorado: profile do projeto é `{profile}` (jargão permitido nesse modo)'
|
|
427
434
|
}
|
|
428
435
|
},
|
|
429
436
|
i18n_add: {
|
|
@@ -505,7 +512,7 @@ module.exports = {
|
|
|
505
512
|
detected: 'Framework detectado: {framework} (installed={installed})',
|
|
506
513
|
q_project_name: 'Nome do projeto',
|
|
507
514
|
q_project_type: 'Tipo do projeto (web_app|api|site|script|dapp|desktop_app)',
|
|
508
|
-
q_profile: 'Perfil: [1] developer [2]
|
|
515
|
+
q_profile: 'Perfil: [1] developer [2] creator [3] team',
|
|
509
516
|
q_use_detected_framework: 'Usar framework detectado? (true/false)',
|
|
510
517
|
q_framework: 'Framework',
|
|
511
518
|
q_framework_installed: 'Framework instalado? (true/false)',
|