@flancer32/skill-adsm-ctx 0.2.1 → 0.3.0
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/CHANGELOG.md +14 -0
- package/README.md +37 -14
- package/bin/adsm-ctx.mjs +44 -0
- package/package.json +65 -11
- package/skill/SKILL.md +2 -2
- package/skill/references/context-lifecycle.md +3 -3
- package/skill/references/document-projections.md +7 -65
- package/skill/references/documentation-quality-audit.md +22 -14
- package/skill/references/semantic-skins.md +65 -0
- package/skill/templates/work/context/semantic-skin.md +11 -22
- package/src/Cli/Command/Design.mjs +17 -0
- package/src/Cli/Command/Init.mjs +22 -0
- package/src/Cli/Command/InstallSkill.mjs +18 -0
- package/src/Cli/Command/Upgrade.mjs +19 -0
- package/src/Cli/Command/Validate.mjs +19 -0
- package/src/Shared/Constants.mjs +0 -4
- package/src/Validate/Service.mjs +14 -178
- package/types.d.ts +5 -2
- package/.github/workflows/flows-app.yaml +0 -95
- package/AGENTS.md +0 -112
- package/bin/cli.mjs +0 -55
- package/ctx/AGENTS.md +0 -44
- package/ctx/README.md +0 -24
- package/ctx/adsm.json +0 -5
- package/ctx/agent/AGENTS.md +0 -41
- package/ctx/agent/flows/.img/flancer32-skill-adsm-ctx-github-flows.png +0 -0
- package/ctx/agent/flows/AGENTS.md +0 -37
- package/ctx/agent/flows/cfg/issues/labeled/accepted/profile.json +0 -25
- package/ctx/agent/flows/cfg/issues/labeled/accepted/prompt.md +0 -104
- package/ctx/agent/flows/cfg/issues/labeled/clarification-provided/profile.json +0 -15
- package/ctx/agent/flows/cfg/issues/labeled/clarification-provided/prompt.md +0 -63
- package/ctx/agent/flows/cfg/issues/opened/admission/profile.json +0 -14
- package/ctx/agent/flows/cfg/issues/opened/admission/prompt.md +0 -110
- package/ctx/agent/flows/cfg/profile.json +0 -40
- package/ctx/agent/flows/cfg/pull_request/closed/merged/profile.json +0 -15
- package/ctx/agent/flows/cfg/pull_request/closed/merged/prompt.md +0 -41
- package/ctx/agent/flows/cfg/pull_request/labeled/needs-fix/profile.json +0 -25
- package/ctx/agent/flows/cfg/pull_request/labeled/needs-fix/prompt.md +0 -79
- package/ctx/agent/flows/cfg/pull_request/labeled/ready-for-validation/profile.json +0 -15
- package/ctx/agent/flows/cfg/pull_request/labeled/ready-for-validation/prompt.md +0 -72
- package/ctx/agent/flows/cfg/pull_request/labeled/verified/profile.json +0 -15
- package/ctx/agent/flows/cfg/pull_request/labeled/verified/prompt.md +0 -48
- package/ctx/agent/flows/deployment.md +0 -116
- package/ctx/agent/flows/labels.md +0 -108
- package/ctx/agent/flows/overview.md +0 -82
- package/ctx/agent/prompt/cli/ai-intro.md +0 -141
- package/ctx/assets/.gitkeep +0 -1
- package/ctx/docs/AGENTS.md +0 -121
- package/ctx/docs/ai-intro.md +0 -37
- package/ctx/docs/architecture/AGENTS.md +0 -98
- package/ctx/docs/architecture/behavior.md +0 -68
- package/ctx/docs/architecture/checklists.md +0 -48
- package/ctx/docs/architecture/constraints.md +0 -53
- package/ctx/docs/architecture/constraints.skin.en.md +0 -38
- package/ctx/docs/architecture/decisions.md +0 -118
- package/ctx/docs/architecture/integration.md +0 -31
- package/ctx/docs/architecture/overview.md +0 -76
- package/ctx/docs/architecture/state.md +0 -50
- package/ctx/docs/architecture/structure.md +0 -38
- package/ctx/docs/architecture/supervision.md +0 -71
- package/ctx/docs/code/AGENTS.md +0 -51
- package/ctx/docs/code/overview.md +0 -53
- package/ctx/docs/code/testing.md +0 -26
- package/ctx/docs/environment/AGENTS.md +0 -50
- package/ctx/docs/environment/overview.md +0 -33
- package/ctx/docs/filesystem.md +0 -44
- package/ctx/docs/product/AGENTS.md +0 -51
- package/ctx/docs/product/domain.md +0 -63
- package/ctx/docs/product/domain.skin.en.md +0 -40
- package/ctx/docs/product/glossary.md +0 -31
- package/ctx/docs/product/overview.md +0 -112
- package/ctx/docs/product/overview.skin.en.md +0 -38
- package/ctx/docs/product/roles.md +0 -17
- package/ctx/docs/product/use-cases.md +0 -15
- package/src/Bootstrap/Cli.mjs +0 -62
- package/src/Bootstrap.mjs +0 -108
- package/test/integration/.gitkeep +0 -1
- package/test/integration/cli-help.test.mjs +0 -15
- package/test/integration/cli-init-validate.test.mjs +0 -223
- package/test/integration/cli-install-skill.test.mjs +0 -41
- package/test/unit/Bootstrap/Cli.test.mjs +0 -23
- package/test/unit/Bootstrap.test.mjs +0 -53
- package/test/unit/Design/Service.test.mjs +0 -11
- package/test/unit/Init/Service.test.mjs +0 -51
- package/test/unit/Shared/Constants.test.mjs +0 -21
- package/test/unit/Shared/CopyDirectory.test.mjs +0 -31
- package/test/unit/Shared/Metadata.test.mjs +0 -9
- package/test/unit/Shared/Path.test.mjs +0 -11
- package/test/unit/Skill/Installer.test.mjs +0 -20
- package/test/unit/Upgrade/Service.test.mjs +0 -13
- package/test/unit/Validate/Service.test.mjs +0 -760
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
# Product Overview Skin
|
|
2
|
-
|
|
3
|
-
- Path: `ctx/docs/product/overview.skin.en.md`
|
|
4
|
-
- Changed: `20260813`
|
|
5
|
-
|
|
6
|
-
## Purpose
|
|
7
|
-
|
|
8
|
-
This skin preserves the high-level product identity of the skill package as a generic ADSM context lifecycle product.
|
|
9
|
-
|
|
10
|
-
## Mental Model
|
|
11
|
-
|
|
12
|
-
The product manages cognitive-context lifecycle rules and guidance, not a downstream business domain.
|
|
13
|
-
|
|
14
|
-
## Scope
|
|
15
|
-
|
|
16
|
-
Includes:
|
|
17
|
-
|
|
18
|
-
- product identity, mission, and scope boundaries;
|
|
19
|
-
- the generic and orchestration-oriented character of the skill;
|
|
20
|
-
- the role split between humans and agents.
|
|
21
|
-
|
|
22
|
-
Excludes:
|
|
23
|
-
|
|
24
|
-
- detailed lifecycle mechanics;
|
|
25
|
-
- internal implementation structure;
|
|
26
|
-
- project-specific consumer-domain semantics.
|
|
27
|
-
|
|
28
|
-
## Invariants
|
|
29
|
-
|
|
30
|
-
- The product manages project cognitive context, not an external consumer project.
|
|
31
|
-
- Agents do default lifecycle work; humans set guardrails, inspect outcomes, and correct drift.
|
|
32
|
-
- The skill must remain generic and must not invent project-specific business or architecture meaning.
|
|
33
|
-
- Installed project-facing templates keep only minimal ADSM bootstrap signals.
|
|
34
|
-
- `overview.md` is an agent document, not a required human entry frame by methodology.
|
|
35
|
-
|
|
36
|
-
## Agent Document
|
|
37
|
-
|
|
38
|
-
`overview.md`
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
# Product Roles
|
|
2
|
-
|
|
3
|
-
- Path: `ctx/docs/product/roles.md`
|
|
4
|
-
- Changed: `20260702`
|
|
5
|
-
|
|
6
|
-
## Roles
|
|
7
|
-
|
|
8
|
-
- **Human Supervisor** — defines goals, sets guardrails, reviews outcomes, and intervenes when agent work drifts from expected product or architecture boundaries.
|
|
9
|
-
- **Agent** — performs most lifecycle work inside declared ADSM boundaries, including analysis, structure changes, audits, and controlled documentation compression.
|
|
10
|
-
- **Developer** — maintains implementation and repository mechanics needed for the product to operate as a skill package.
|
|
11
|
-
- **Context Maintainer** — evolves templates, validation rules, documentation quality audit guidance, documentation compression guidance, and upgrade conventions.
|
|
12
|
-
|
|
13
|
-
## Authority Pattern
|
|
14
|
-
|
|
15
|
-
- The human supervisor owns direction, acceptance, and boundary expansion.
|
|
16
|
-
- The agent owns default operational execution inside documented boundaries.
|
|
17
|
-
- The developer and context maintainer may be the same human, but their responsibilities remain distinct from the autonomous agent role.
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
# Product Use Cases
|
|
2
|
-
|
|
3
|
-
- Path: `ctx/docs/product/use-cases.md`
|
|
4
|
-
- Changed: `20260708`
|
|
5
|
-
|
|
6
|
-
## Use Cases
|
|
7
|
-
|
|
8
|
-
- Initialize cognitive context in a repository so agents have a baseline `ctx/` structure, metadata, and local routing files.
|
|
9
|
-
- Install the bundled skill payload into a caller-provided skills root so an agent skill host can load `adsm-ctx`.
|
|
10
|
-
- Assess whether an existing repository has the required ADSM baseline, documentation levels, metadata, and projection shape.
|
|
11
|
-
- Audit an existing documentation corpus against ADSM documentation quality criteria and produce evidence-backed findings.
|
|
12
|
-
- Compress an existing `ctx/docs/` corpus while preserving accepted project meaning and skin-controlled meaning in matching agent documents.
|
|
13
|
-
- Define or revise the reserved `ctx/agent/` boundary while leaving project-local tool materials outside the ADSM baseline.
|
|
14
|
-
- Route documentation work toward specialized skills when project-specific context requires narrower expertise.
|
|
15
|
-
- Upgrade a context when conventions or template structure change while preserving project-specific content.
|
package/src/Bootstrap/Cli.mjs
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
// @ts-check
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @namespace Fl32_Skill_Adsm_Ctx_Bootstrap_Cli
|
|
5
|
-
* @description Parses CLI arguments for the ADSM context lifecycle application.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
const usage = [
|
|
9
|
-
"Usage: adsm-ctx <command> [target-path]",
|
|
10
|
-
"Commands:",
|
|
11
|
-
" init [target-path]",
|
|
12
|
-
" design [target-path]",
|
|
13
|
-
" validate [target-path]",
|
|
14
|
-
" upgrade [target-path]",
|
|
15
|
-
" install-skill <skills-root>",
|
|
16
|
-
" --help",
|
|
17
|
-
].join("\n");
|
|
18
|
-
|
|
19
|
-
export default class Cli {
|
|
20
|
-
/**
|
|
21
|
-
* @param {object} deps
|
|
22
|
-
* @param {Fl32_Skill_Adsm_Ctx_Shared_Path} deps.pathService
|
|
23
|
-
*/
|
|
24
|
-
constructor({pathService}) {
|
|
25
|
-
/**
|
|
26
|
-
* Parses CLI arguments.
|
|
27
|
-
*
|
|
28
|
-
* @param {object} params
|
|
29
|
-
* @param {string[]} params.cliArgs
|
|
30
|
-
* @param {string} params.projectRoot
|
|
31
|
-
* @returns {{
|
|
32
|
-
* command?: string,
|
|
33
|
-
* errors: string[],
|
|
34
|
-
* helpRequested: boolean,
|
|
35
|
-
* targetPath: string,
|
|
36
|
-
* usage: string
|
|
37
|
-
* }}
|
|
38
|
-
*/
|
|
39
|
-
this.parse = function ({cliArgs = [], projectRoot}) {
|
|
40
|
-
const [command, targetArg] = cliArgs;
|
|
41
|
-
const errors = [];
|
|
42
|
-
const helpRequested = !command || command === "--help" || command === "-h";
|
|
43
|
-
const targetPath = targetArg ? pathService.resolve(targetArg) : projectRoot;
|
|
44
|
-
|
|
45
|
-
if (!helpRequested) {
|
|
46
|
-
const supported = new Set(["init", "design", "validate", "upgrade", "install-skill"]);
|
|
47
|
-
if (!supported.has(command)) {
|
|
48
|
-
errors.push(`Unknown command: ${command}`);
|
|
49
|
-
}
|
|
50
|
-
if (command === "install-skill" && !targetArg) {
|
|
51
|
-
errors.push("Usage: adsm-ctx install-skill <skills-root>");
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
return {command, errors, helpRequested, targetPath, usage};
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export const __deps__ = Object.freeze({
|
|
61
|
-
pathService: "Fl32_Skill_Adsm_Ctx_Shared_Path$",
|
|
62
|
-
});
|
package/src/Bootstrap.mjs
DELETED
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
// @ts-check
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @namespace Fl32_Skill_Adsm_Ctx_Bootstrap
|
|
5
|
-
* @description TeqFW root CLI application component for ADSM context lifecycle operations.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
export default class Bootstrap {
|
|
9
|
-
/**
|
|
10
|
-
* @param {object} deps
|
|
11
|
-
* @param {Fl32_Skill_Adsm_Ctx_Bootstrap_Cli} deps.cli
|
|
12
|
-
* @param {Fl32_Skill_Adsm_Ctx_Design_Service} deps.design
|
|
13
|
-
* @param {Fl32_Skill_Adsm_Ctx_Init_Service} deps.init
|
|
14
|
-
* @param {Fl32_Skill_Adsm_Ctx_Skill_Installer} deps.installer
|
|
15
|
-
* @param {Fl32_Skill_Adsm_Ctx_Upgrade_Service} deps.upgrade
|
|
16
|
-
* @param {Fl32_Skill_Adsm_Ctx_Validate_Service} deps.validate
|
|
17
|
-
*/
|
|
18
|
-
constructor({cli, design, init, installer, upgrade, validate}) {
|
|
19
|
-
let stopping = false;
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Runs the CLI application once.
|
|
23
|
-
*
|
|
24
|
-
* @param {object} params
|
|
25
|
-
* @param {string[]} params.cliArgs
|
|
26
|
-
* @param {string} params.projectRoot
|
|
27
|
-
* @returns {Promise<number>}
|
|
28
|
-
*/
|
|
29
|
-
this.run = async ({cliArgs = [], projectRoot}) => {
|
|
30
|
-
const parsed = cli.parse({cliArgs, projectRoot});
|
|
31
|
-
|
|
32
|
-
if (parsed.helpRequested) {
|
|
33
|
-
process.stderr.write(`${parsed.usage}\n`);
|
|
34
|
-
return 0;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
if (parsed.errors.length > 0) {
|
|
38
|
-
process.stderr.write(`${parsed.errors.join("\n")}\n`);
|
|
39
|
-
process.stderr.write(`${parsed.usage}\n`);
|
|
40
|
-
return 2;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
if (parsed.command === "init") {
|
|
44
|
-
const result = await init.execute({targetPath: parsed.targetPath});
|
|
45
|
-
process.stdout.write(`${formatInitReport(result)}\n`);
|
|
46
|
-
return 0;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
if (parsed.command === "design") {
|
|
50
|
-
process.stdout.write(`${await design.execute({targetPath: parsed.targetPath})}\n`);
|
|
51
|
-
return 0;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (parsed.command === "validate") {
|
|
55
|
-
const result = await validate.execute({targetPath: parsed.targetPath});
|
|
56
|
-
process.stdout.write(`${validate.format({result})}\n`);
|
|
57
|
-
return result.ok ? 0 : 1;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
if (parsed.command === "upgrade") {
|
|
61
|
-
const result = await upgrade.execute({targetPath: parsed.targetPath});
|
|
62
|
-
process.stdout.write(`${result.message}\n`);
|
|
63
|
-
return result.ok ? 0 : 1;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
if (parsed.command === "install-skill") {
|
|
67
|
-
const targetDir = await installer.execute({targetRoot: parsed.targetPath});
|
|
68
|
-
process.stdout.write(`Installed skill payload to ${targetDir}\n`);
|
|
69
|
-
return 0;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
process.stderr.write(`${parsed.usage}\n`);
|
|
73
|
-
return 2;
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Stops the application.
|
|
78
|
-
*
|
|
79
|
-
* @returns {Promise<void>}
|
|
80
|
-
*/
|
|
81
|
-
this.stop = async () => {
|
|
82
|
-
if (stopping) return;
|
|
83
|
-
stopping = true;
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* @param {{ctxRoot: string, created: string[], skipped: string[]}} result
|
|
90
|
-
* @returns {string}
|
|
91
|
-
*/
|
|
92
|
-
function formatInitReport(result) {
|
|
93
|
-
const lines = [
|
|
94
|
-
`Initialized ADSM context in ${result.ctxRoot}`,
|
|
95
|
-
...result.created.map((entry) => `Created: ${entry}`),
|
|
96
|
-
...result.skipped.map((entry) => `Skipped: ${entry}`),
|
|
97
|
-
];
|
|
98
|
-
return lines.join("\n");
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
export const __deps__ = Object.freeze({
|
|
102
|
-
cli: "Fl32_Skill_Adsm_Ctx_Bootstrap_Cli$",
|
|
103
|
-
design: "Fl32_Skill_Adsm_Ctx_Design_Service$",
|
|
104
|
-
init: "Fl32_Skill_Adsm_Ctx_Init_Service$",
|
|
105
|
-
installer: "Fl32_Skill_Adsm_Ctx_Skill_Installer$",
|
|
106
|
-
upgrade: "Fl32_Skill_Adsm_Ctx_Upgrade_Service$",
|
|
107
|
-
validate: "Fl32_Skill_Adsm_Ctx_Validate_Service$",
|
|
108
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import test from 'node:test';
|
|
2
|
-
import assert from 'node:assert/strict';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
import {spawnSync} from 'node:child_process';
|
|
5
|
-
import {fileURLToPath} from 'node:url';
|
|
6
|
-
|
|
7
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
-
const __dirname = path.dirname(__filename);
|
|
9
|
-
const cliPath = path.join(__dirname, '..', '..', 'bin', 'cli.mjs');
|
|
10
|
-
|
|
11
|
-
test('cli help works', () => {
|
|
12
|
-
const result = spawnSync('node', [cliPath, '--help'], {encoding: 'utf8'});
|
|
13
|
-
assert.equal(result.status, 0);
|
|
14
|
-
assert.match(result.stderr, /Usage: adsm-ctx/);
|
|
15
|
-
});
|
|
@@ -1,223 +0,0 @@
|
|
|
1
|
-
import test from 'node:test';
|
|
2
|
-
import assert from 'node:assert/strict';
|
|
3
|
-
import fs from 'node:fs';
|
|
4
|
-
import path from 'node:path';
|
|
5
|
-
import {spawnSync} from 'node:child_process';
|
|
6
|
-
import {fileURLToPath} from 'node:url';
|
|
7
|
-
|
|
8
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
-
const __dirname = path.dirname(__filename);
|
|
10
|
-
const cliPath = path.join(__dirname, '..', '..', 'bin', 'cli.mjs');
|
|
11
|
-
|
|
12
|
-
test('cli init creates files in current working directory by default', () => {
|
|
13
|
-
const sandboxRoot = fs.mkdtempSync(path.join('/tmp', 'adsm-ctx-cli-init-'));
|
|
14
|
-
const result = spawnSync('node', [cliPath, 'init'], {cwd: sandboxRoot, encoding: 'utf8'});
|
|
15
|
-
|
|
16
|
-
try {
|
|
17
|
-
assert.equal(result.status, 0, result.stderr);
|
|
18
|
-
assert.ok(fs.existsSync(path.join(sandboxRoot, 'ctx', 'adsm.json')));
|
|
19
|
-
const filesystemDoc = fs.readFileSync(path.join(sandboxRoot, 'ctx', 'docs', 'filesystem.md'), 'utf8');
|
|
20
|
-
const productDomainDoc = fs.readFileSync(path.join(sandboxRoot, 'ctx', 'docs', 'product', 'domain.md'), 'utf8');
|
|
21
|
-
assert.match(filesystemDoc, /This starter is not a completed repository map/);
|
|
22
|
-
assert.doesNotMatch(filesystemDoc, /npm package repository/);
|
|
23
|
-
assert.doesNotMatch(filesystemDoc, /node_modules/);
|
|
24
|
-
assert.match(productDomainDoc, /- Changed: `\d{8}`/);
|
|
25
|
-
assert.doesNotMatch(productDomainDoc, /- Changed: `YYYYMMDD`/);
|
|
26
|
-
assert.match(result.stdout, /Initialized ADSM context in /);
|
|
27
|
-
} finally {
|
|
28
|
-
fs.rmSync(sandboxRoot, {recursive: true, force: true});
|
|
29
|
-
}
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
test('cli validate succeeds after init in a clean project', () => {
|
|
33
|
-
const sandboxRoot = fs.mkdtempSync(path.join('/tmp', 'adsm-ctx-cli-validate-ok-'));
|
|
34
|
-
|
|
35
|
-
try {
|
|
36
|
-
const initResult = spawnSync('node', [cliPath, 'init'], {cwd: sandboxRoot, encoding: 'utf8'});
|
|
37
|
-
const validateResult = spawnSync('node', [cliPath, 'validate'], {cwd: sandboxRoot, encoding: 'utf8'});
|
|
38
|
-
|
|
39
|
-
assert.equal(initResult.status, 0, initResult.stderr);
|
|
40
|
-
assert.equal(validateResult.status, 0, validateResult.stderr);
|
|
41
|
-
assert.match(validateResult.stdout, /ADSM context validation passed/);
|
|
42
|
-
assert.match(validateResult.stdout, /Errors: 0/);
|
|
43
|
-
assert.match(validateResult.stdout, /Warnings: 0/);
|
|
44
|
-
} finally {
|
|
45
|
-
fs.rmSync(sandboxRoot, {recursive: true, force: true});
|
|
46
|
-
}
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
test('cli init does not overwrite existing metadata and validate succeeds after init', () => {
|
|
50
|
-
const sandboxRoot = fs.mkdtempSync(path.join('/tmp', 'adsm-ctx-cli-safe-init-'));
|
|
51
|
-
|
|
52
|
-
try {
|
|
53
|
-
fs.mkdirSync(path.join(sandboxRoot, 'ctx'), {recursive: true});
|
|
54
|
-
fs.writeFileSync(path.join(sandboxRoot, 'ctx', 'adsm.json'), '{"schema":"custom","version":"9.9.9"}\n');
|
|
55
|
-
|
|
56
|
-
const initResult = spawnSync('node', [cliPath, 'init'], {cwd: sandboxRoot, encoding: 'utf8'});
|
|
57
|
-
const validateResult = spawnSync('node', [cliPath, 'validate'], {cwd: sandboxRoot, encoding: 'utf8'});
|
|
58
|
-
|
|
59
|
-
assert.equal(initResult.status, 0, initResult.stderr);
|
|
60
|
-
assert.match(fs.readFileSync(path.join(sandboxRoot, 'ctx', 'adsm.json'), 'utf8'), /"schema":"custom"/);
|
|
61
|
-
assert.equal(validateResult.status, 1);
|
|
62
|
-
assert.match(validateResult.stdout, /Unexpected schema: custom/);
|
|
63
|
-
} finally {
|
|
64
|
-
fs.rmSync(sandboxRoot, {recursive: true, force: true});
|
|
65
|
-
}
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
test('cli validate reports missing context in empty directory', () => {
|
|
69
|
-
const sandboxRoot = fs.mkdtempSync(path.join('/tmp', 'adsm-ctx-cli-empty-'));
|
|
70
|
-
|
|
71
|
-
try {
|
|
72
|
-
const result = spawnSync('node', [cliPath, 'validate'], {cwd: sandboxRoot, encoding: 'utf8'});
|
|
73
|
-
assert.equal(result.status, 1);
|
|
74
|
-
assert.match(result.stdout, /Missing: ctx/);
|
|
75
|
-
assert.match(result.stdout, /Missing ctx\/adsm\.json/);
|
|
76
|
-
} finally {
|
|
77
|
-
fs.rmSync(sandboxRoot, {recursive: true, force: true});
|
|
78
|
-
}
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
test('cli validate reports invalid skin structure and reference', () => {
|
|
82
|
-
const sandboxRoot = fs.mkdtempSync(path.join('/tmp', 'adsm-ctx-cli-invalid-skin-'));
|
|
83
|
-
|
|
84
|
-
try {
|
|
85
|
-
const initResult = spawnSync('node', [cliPath, 'init'], {cwd: sandboxRoot, encoding: 'utf8'});
|
|
86
|
-
assert.equal(initResult.status, 0, initResult.stderr);
|
|
87
|
-
|
|
88
|
-
fs.writeFileSync(path.join(sandboxRoot, 'ctx', 'docs', 'product', 'domain.skin.en.md'), [
|
|
89
|
-
'# Product Domain Skin',
|
|
90
|
-
'',
|
|
91
|
-
'- Path: `ctx/docs/product/domain.skin.en.md`',
|
|
92
|
-
'- Changed: `20260708`',
|
|
93
|
-
'',
|
|
94
|
-
'## Purpose',
|
|
95
|
-
'',
|
|
96
|
-
'Human-facing control.',
|
|
97
|
-
'',
|
|
98
|
-
'## Scope',
|
|
99
|
-
'',
|
|
100
|
-
'Includes:',
|
|
101
|
-
'',
|
|
102
|
-
'- domain identity.',
|
|
103
|
-
'',
|
|
104
|
-
'## Invariants',
|
|
105
|
-
'',
|
|
106
|
-
'- Keep the domain stable.',
|
|
107
|
-
'- invariant 1.',
|
|
108
|
-
'- invariant 2.',
|
|
109
|
-
'- invariant 3.',
|
|
110
|
-
'- invariant 4.',
|
|
111
|
-
'- invariant 5.',
|
|
112
|
-
'- invariant 6.',
|
|
113
|
-
'- invariant 7.',
|
|
114
|
-
'- invariant 8.',
|
|
115
|
-
'- invariant 9.',
|
|
116
|
-
'- invariant 10.',
|
|
117
|
-
'- invariant 11.',
|
|
118
|
-
'- invariant 12.',
|
|
119
|
-
'',
|
|
120
|
-
'## Agent Document',
|
|
121
|
-
'',
|
|
122
|
-
'`other.md`',
|
|
123
|
-
'',
|
|
124
|
-
].join('\n'));
|
|
125
|
-
|
|
126
|
-
const result = spawnSync('node', [cliPath, 'validate'], {cwd: sandboxRoot, encoding: 'utf8'});
|
|
127
|
-
assert.equal(result.status, 1);
|
|
128
|
-
assert.match(result.stdout, /Missing skin section "Mental Model": ctx\/docs\/product\/domain\.skin\.en\.md/);
|
|
129
|
-
assert.match(result.stdout, /Missing Excludes: block in Scope: ctx\/docs\/product\/domain\.skin\.en\.md/);
|
|
130
|
-
assert.match(result.stdout, /Skin agent document mismatch: ctx\/docs\/product\/domain\.skin\.en\.md references ctx\/docs\/product\/other\.md but expected ctx\/docs\/product\/domain\.md/);
|
|
131
|
-
} finally {
|
|
132
|
-
fs.rmSync(sandboxRoot, {recursive: true, force: true});
|
|
133
|
-
}
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
test('cli validate reports broken local markdown links as errors', () => {
|
|
137
|
-
const sandboxRoot = fs.mkdtempSync(path.join('/tmp', 'adsm-ctx-cli-broken-link-'));
|
|
138
|
-
|
|
139
|
-
try {
|
|
140
|
-
const initResult = spawnSync('node', [cliPath, 'init'], {cwd: sandboxRoot, encoding: 'utf8'});
|
|
141
|
-
assert.equal(initResult.status, 0, initResult.stderr);
|
|
142
|
-
|
|
143
|
-
fs.writeFileSync(path.join(sandboxRoot, 'ctx', 'docs', 'product', 'overview.md'), [
|
|
144
|
-
'# Product Overview',
|
|
145
|
-
'',
|
|
146
|
-
'- Path: `ctx/docs/product/overview.md`',
|
|
147
|
-
'- Changed: `20260708`',
|
|
148
|
-
'',
|
|
149
|
-
'See [domain](./missing.md).',
|
|
150
|
-
'',
|
|
151
|
-
].join('\n'));
|
|
152
|
-
|
|
153
|
-
const result = spawnSync('node', [cliPath, 'validate'], {cwd: sandboxRoot, encoding: 'utf8'});
|
|
154
|
-
assert.equal(result.status, 1);
|
|
155
|
-
assert.match(result.stdout, /Missing linked document: \.\/missing\.md resolves to ctx\/docs\/product\/missing\.md/);
|
|
156
|
-
assert.match(result.stdout, /Errors: [1-9]/);
|
|
157
|
-
} finally {
|
|
158
|
-
fs.rmSync(sandboxRoot, {recursive: true, force: true});
|
|
159
|
-
}
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
test('cli validate succeeds with warnings only', () => {
|
|
163
|
-
const sandboxRoot = fs.mkdtempSync(path.join('/tmp', 'adsm-ctx-cli-warning-only-'));
|
|
164
|
-
|
|
165
|
-
try {
|
|
166
|
-
const initResult = spawnSync('node', [cliPath, 'init'], {cwd: sandboxRoot, encoding: 'utf8'});
|
|
167
|
-
assert.equal(initResult.status, 0, initResult.stderr);
|
|
168
|
-
|
|
169
|
-
fs.writeFileSync(path.join(sandboxRoot, 'ctx', 'docs', 'product', 'domain.skin.en.md'), [
|
|
170
|
-
'# Product Domain Skin',
|
|
171
|
-
'',
|
|
172
|
-
'- Path: `ctx/docs/product/domain.skin.en.md`',
|
|
173
|
-
'- Changed: `20260708`',
|
|
174
|
-
'',
|
|
175
|
-
'## Purpose',
|
|
176
|
-
'',
|
|
177
|
-
'See [domain](./domain.md#purpose) while keeping the human-facing summary compact.',
|
|
178
|
-
'',
|
|
179
|
-
'## Mental Model',
|
|
180
|
-
'',
|
|
181
|
-
'This is the human semantic frame.',
|
|
182
|
-
'',
|
|
183
|
-
'## Scope',
|
|
184
|
-
'',
|
|
185
|
-
'Includes:',
|
|
186
|
-
'',
|
|
187
|
-
'- domain identity.',
|
|
188
|
-
'',
|
|
189
|
-
'Excludes:',
|
|
190
|
-
'',
|
|
191
|
-
'- operational overload.',
|
|
192
|
-
'',
|
|
193
|
-
'## Invariants',
|
|
194
|
-
'',
|
|
195
|
-
'- Keep the domain stable.',
|
|
196
|
-
'- invariant 1.',
|
|
197
|
-
'- invariant 2.',
|
|
198
|
-
'- invariant 3.',
|
|
199
|
-
'- invariant 4.',
|
|
200
|
-
'- invariant 5.',
|
|
201
|
-
'- invariant 6.',
|
|
202
|
-
'- invariant 7.',
|
|
203
|
-
'- invariant 8.',
|
|
204
|
-
'- invariant 9.',
|
|
205
|
-
'- invariant 10.',
|
|
206
|
-
'- invariant 11.',
|
|
207
|
-
'- invariant 12.',
|
|
208
|
-
'',
|
|
209
|
-
'## Agent Document',
|
|
210
|
-
'',
|
|
211
|
-
'`domain.md`',
|
|
212
|
-
'',
|
|
213
|
-
].join('\n'));
|
|
214
|
-
|
|
215
|
-
const result = spawnSync('node', [cliPath, 'validate'], {cwd: sandboxRoot, encoding: 'utf8'});
|
|
216
|
-
assert.equal(result.status, 0, result.stderr);
|
|
217
|
-
assert.match(result.stdout, /passed with warnings/i);
|
|
218
|
-
assert.match(result.stdout, /Errors: 0/);
|
|
219
|
-
assert.match(result.stdout, /Warnings: [1-9]/);
|
|
220
|
-
} finally {
|
|
221
|
-
fs.rmSync(sandboxRoot, {recursive: true, force: true});
|
|
222
|
-
}
|
|
223
|
-
});
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import test from 'node:test';
|
|
2
|
-
import assert from 'node:assert/strict';
|
|
3
|
-
import fs from 'node:fs';
|
|
4
|
-
import path from 'node:path';
|
|
5
|
-
import {spawnSync} from 'node:child_process';
|
|
6
|
-
import {fileURLToPath} from 'node:url';
|
|
7
|
-
|
|
8
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
-
const __dirname = path.dirname(__filename);
|
|
10
|
-
const cliPath = path.join(__dirname, '..', '..', 'bin', 'cli.mjs');
|
|
11
|
-
|
|
12
|
-
test('cli install-skill installs current payload and replaces stale target content', () => {
|
|
13
|
-
const sandboxRoot = fs.mkdtempSync(path.join('/tmp', 'adsm-ctx-cli-install-skill-'));
|
|
14
|
-
const skillDir = path.join(sandboxRoot, 'adsm-ctx');
|
|
15
|
-
const staleFile = path.join(skillDir, 'stale.txt');
|
|
16
|
-
const installedSkill = path.join(skillDir, 'SKILL.md');
|
|
17
|
-
const installedReference = path.join(skillDir, 'references', 'context-lifecycle.md');
|
|
18
|
-
const installedTemplate = path.join(skillDir, 'templates', 'ctx', 'docs', 'architecture', 'overview.md');
|
|
19
|
-
|
|
20
|
-
try {
|
|
21
|
-
fs.mkdirSync(skillDir, {recursive: true});
|
|
22
|
-
fs.writeFileSync(staleFile, 'obsolete payload\n');
|
|
23
|
-
fs.writeFileSync(installedSkill, 'outdated skill\n');
|
|
24
|
-
|
|
25
|
-
const result = spawnSync('node', [cliPath, 'install-skill', sandboxRoot], {encoding: 'utf8'});
|
|
26
|
-
|
|
27
|
-
assert.equal(result.status, 0, result.stderr);
|
|
28
|
-
assert.match(result.stdout, new RegExp(`Installed skill payload to ${escapeRegExp(skillDir)}`));
|
|
29
|
-
assert.ok(fs.existsSync(installedSkill));
|
|
30
|
-
assert.ok(fs.existsSync(installedReference));
|
|
31
|
-
assert.ok(fs.existsSync(installedTemplate));
|
|
32
|
-
assert.match(fs.readFileSync(installedSkill, 'utf8'), /name: adsm-ctx/);
|
|
33
|
-
assert.equal(fs.existsSync(staleFile), false);
|
|
34
|
-
} finally {
|
|
35
|
-
fs.rmSync(sandboxRoot, {recursive: true, force: true});
|
|
36
|
-
}
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
function escapeRegExp(value) {
|
|
40
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
41
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import test from 'node:test';
|
|
2
|
-
import assert from 'node:assert/strict';
|
|
3
|
-
|
|
4
|
-
import Cli from '../../../src/Bootstrap/Cli.mjs';
|
|
5
|
-
|
|
6
|
-
test('Cli uses project root as default target path', () => {
|
|
7
|
-
const cli = new Cli({pathService: {resolve: (value) => `/abs/${value}`}});
|
|
8
|
-
const result = cli.parse({cliArgs: ['init'], projectRoot: '/cwd'});
|
|
9
|
-
assert.equal(result.targetPath, '/cwd');
|
|
10
|
-
assert.equal(result.command, 'init');
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
test('Cli resolves explicit target path', () => {
|
|
14
|
-
const cli = new Cli({pathService: {resolve: (value) => `/abs/${value}`}});
|
|
15
|
-
const result = cli.parse({cliArgs: ['validate', 'demo'], projectRoot: '/cwd'});
|
|
16
|
-
assert.equal(result.targetPath, '/abs/demo');
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
test('Cli requires target root for install-skill', () => {
|
|
20
|
-
const cli = new Cli({pathService: {resolve: (value) => value}});
|
|
21
|
-
const result = cli.parse({cliArgs: ['install-skill'], projectRoot: '/cwd'});
|
|
22
|
-
assert.match(result.errors.join('\n'), /install-skill <skills-root>/);
|
|
23
|
-
});
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import test from 'node:test';
|
|
2
|
-
import assert from 'node:assert/strict';
|
|
3
|
-
|
|
4
|
-
import Bootstrap from '../../src/Bootstrap.mjs';
|
|
5
|
-
|
|
6
|
-
test('Bootstrap routes init command and reports created and skipped entries', async () => {
|
|
7
|
-
const writes = {stdout: [], stderr: []};
|
|
8
|
-
const stdoutWrite = process.stdout.write;
|
|
9
|
-
const stderrWrite = process.stderr.write;
|
|
10
|
-
process.stdout.write = (chunk) => { writes.stdout.push(String(chunk)); return true; };
|
|
11
|
-
process.stderr.write = (chunk) => { writes.stderr.push(String(chunk)); return true; };
|
|
12
|
-
|
|
13
|
-
try {
|
|
14
|
-
const app = new Bootstrap({
|
|
15
|
-
cli: {parse: () => ({command: 'init', errors: [], helpRequested: false, targetPath: '/project', usage: 'usage'})},
|
|
16
|
-
design: {execute: async () => ''},
|
|
17
|
-
init: {execute: async () => ({ctxRoot: '/project/ctx', created: ['ctx'], skipped: ['ctx/adsm.json']})},
|
|
18
|
-
installer: {execute: async () => ''},
|
|
19
|
-
upgrade: {execute: async () => ({ok: true, message: ''})},
|
|
20
|
-
validate: {execute: async () => ({ok: true}), format: () => ''},
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
const code = await app.run({cliArgs: ['init'], projectRoot: '/project'});
|
|
24
|
-
assert.equal(code, 0);
|
|
25
|
-
assert.match(writes.stdout.join(''), /Created: ctx/);
|
|
26
|
-
assert.match(writes.stdout.join(''), /Skipped: ctx\/adsm\.json/);
|
|
27
|
-
} finally {
|
|
28
|
-
process.stdout.write = stdoutWrite;
|
|
29
|
-
process.stderr.write = stderrWrite;
|
|
30
|
-
}
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
test('Bootstrap prints help and exits with zero', async () => {
|
|
34
|
-
const writes = {stdout: [], stderr: []};
|
|
35
|
-
const stderrWrite = process.stderr.write;
|
|
36
|
-
process.stderr.write = (chunk) => { writes.stderr.push(String(chunk)); return true; };
|
|
37
|
-
|
|
38
|
-
try {
|
|
39
|
-
const app = new Bootstrap({
|
|
40
|
-
cli: {parse: () => ({errors: [], helpRequested: true, usage: 'usage'})},
|
|
41
|
-
design: {execute: async () => ''},
|
|
42
|
-
init: {execute: async () => ({})},
|
|
43
|
-
installer: {execute: async () => ''},
|
|
44
|
-
upgrade: {execute: async () => ({ok: true, message: ''})},
|
|
45
|
-
validate: {execute: async () => ({ok: true}), format: () => ''},
|
|
46
|
-
});
|
|
47
|
-
const code = await app.run({cliArgs: ['--help'], projectRoot: '/project'});
|
|
48
|
-
assert.equal(code, 0);
|
|
49
|
-
assert.equal(writes.stderr.join(''), 'usage\n');
|
|
50
|
-
} finally {
|
|
51
|
-
process.stderr.write = stderrWrite;
|
|
52
|
-
}
|
|
53
|
-
});
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import test from 'node:test';
|
|
2
|
-
import assert from 'node:assert/strict';
|
|
3
|
-
|
|
4
|
-
import Service from '../../../src/Design/Service.mjs';
|
|
5
|
-
|
|
6
|
-
test('Design service references questionnaire and orchestration', async () => {
|
|
7
|
-
const service = new Service();
|
|
8
|
-
const result = await service.execute({targetPath: '/project'});
|
|
9
|
-
assert.match(result, /design-questionnaire\.md/);
|
|
10
|
-
assert.match(result, /specialized ADSM skills/);
|
|
11
|
-
});
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import test from 'node:test';
|
|
2
|
-
import assert from 'node:assert/strict';
|
|
3
|
-
|
|
4
|
-
import Service from '../../../src/Init/Service.mjs';
|
|
5
|
-
|
|
6
|
-
test('Init service creates metadata only when missing', async () => {
|
|
7
|
-
const writes = [];
|
|
8
|
-
const fs = {
|
|
9
|
-
existing: new Set(),
|
|
10
|
-
existsSync(path) { return this.existing.has(path); },
|
|
11
|
-
mkdirSync(path) { this.existing.add(path); },
|
|
12
|
-
writeFileSync(path, content) { this.existing.add(path); writes.push({path, content}); },
|
|
13
|
-
};
|
|
14
|
-
const copyDirectory = {
|
|
15
|
-
copyMissing: ({created}) => created.push('ctx'),
|
|
16
|
-
};
|
|
17
|
-
const service = new Service({
|
|
18
|
-
copyDirectory,
|
|
19
|
-
fs,
|
|
20
|
-
metadata: {create: () => ({schema: 'adsm-context', version: '0.1.0'})},
|
|
21
|
-
pathService: {join: (...parts) => parts.join('/'), resolve: (value) => value},
|
|
22
|
-
url: {fileURLToPath: () => '/templates/ctx'},
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
const result = await service.execute({targetPath: '/project'});
|
|
26
|
-
assert.equal(result.projectRoot, '/project');
|
|
27
|
-
assert.ok(result.created.includes('ctx'));
|
|
28
|
-
assert.ok(result.created.includes('AGENTS.md'));
|
|
29
|
-
assert.ok(result.created.includes('ctx/adsm.json'));
|
|
30
|
-
assert.match(writes.find((item) => item.path === '/project/ctx/adsm.json').content, /"version": "0\.1\.0"/);
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
test('Init service skips existing root agents and metadata', async () => {
|
|
34
|
-
const fs = {
|
|
35
|
-
existing: new Set(['/project/AGENTS.md', '/project/ctx/adsm.json']),
|
|
36
|
-
existsSync(path) { return this.existing.has(path); },
|
|
37
|
-
mkdirSync() {},
|
|
38
|
-
writeFileSync() { throw new Error('unexpected write'); },
|
|
39
|
-
};
|
|
40
|
-
const service = new Service({
|
|
41
|
-
copyDirectory: {copyMissing() {}},
|
|
42
|
-
fs,
|
|
43
|
-
metadata: {create: () => ({schema: 'adsm-context', version: '0.1.0'})},
|
|
44
|
-
pathService: {join: (...parts) => parts.join('/'), resolve: (value) => value},
|
|
45
|
-
url: {fileURLToPath: () => '/templates/ctx'},
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
const result = await service.execute({targetPath: '/project'});
|
|
49
|
-
assert.ok(result.skipped.includes('AGENTS.md'));
|
|
50
|
-
assert.ok(result.skipped.includes('ctx/adsm.json'));
|
|
51
|
-
});
|